import json
import jsonpath
import time
import re
import importlib
import ast
import inspect
from functools import partial

from apps.scripts.models import PythonScript
from apps.tests.models import CaseRunLog
from apps.envs.models import EnvGlobalParams


def exec_and_return(code, globals_map=None, locals_map=None):
    if globals_map is None:
        globals_map = {}
    if locals_map is None:
        locals_map = {}
    exec(code, globals_map, locals_map)
    return locals_map


def get_env_params_by_env_id(env_id):
    """
    通过项目ID和环境ID获取环境下所有变量
    """
    return EnvGlobalParams.objects.all().filter(env=env_id, is_delete=False)


def get_module_functions(module_path):
    return dict(inspect.getmembers(importlib.import_module(module_path), inspect.isfunction))


def loop_for_iterator(item):
    """
    循环执行迭代器中的内容
    """
    while True:
        try:
            next(item)
        except StopIteration:
            break


def extract_by_jsonpath(input_data, json_path):
    extract_data = jsonpath.jsonpath(input_data, json_path)
    if extract_data:
        return True, extract_data[0]
    return False, f'表达式：{json_path} 未匹配到结果'


def formatter_log(log_type, log_content):
    local_time = time.strftime('%Y-%m-%d %H:%M:%S')
    return '【{}】【{}】{}'.format(local_time, log_type, log_content)


def list_to_dict(list_obj: list):
    """
    将列表嵌套字典转换成dict
    """
    if list_obj:
        return {list_dict_obj.key: list_dict_obj.value for list_dict_obj in list_obj}
    else:
        return dict()


def json_to_dict(json_str):
    if json_str:
        return json.loads(json_str)
    return dict()


def str_to_number_and_bool(case_logs_obj, params_list, int_pattern='int[(].*?[)]', float_pattern='float[(].*?[)]',
                           bool_pattern='bool[(].*?[)]', list_pattern='list[(].*?[)]', dict_pattern='dict[(].*?[)]'):
    """
    解析函数中传入的参数并返回解析后参数的元组
    """
    for index, var in enumerate(params_list):
        bool_match = re.findall(bool_pattern, var)
        float_match = re.findall(float_pattern, var)
        int_match = re.findall(int_pattern, var)
        list_match = re.findall(list_pattern, var)
        dict_match = re.findall(dict_pattern, var)
        try:
            if int_match:
                params_list[index] = ast.literal_eval(int_match[0][4:-1])
            if float_match:
                params_list[index] = ast.literal_eval(float_match[0][6:-1])
            if bool_match or list_match or dict_match:
                params_list[index] = ast.literal_eval(bool_match[0][5:-1])
        except Exception as e:
            case_logs_obj.result = CaseRunLog.CaseResult.ERROR
            case_logs_obj.logs.append(
                formatter_log('error', '【类型转换错误：{}】'.format(e)))
            raise e

    return tuple(params_list)


def replace_func_value(params_class: object, case_logs_obj, value, inner_funcs, f_pattern=r'f{.*?}'):
    params_key = params_obj['key']
    params_value = params_obj['value']
    match_all = re.findall(f_pattern, params_value)
    # 只匹配到了一个函数
    if match_all and len(match_all) == 1 and len(params_value) == len(match_all[0]):
        match_split = match_all[0][2:-1].split(',')
        match_func_name = match_split[0]
        match_func_params = str_to_number_and_bool(case_logs_obj, match_split[1:])
        if inner_funcs.get(match_func_name):
            func_return_value = inner_funcs[match_func_name](*match_func_params)
        elif PythonScript.objects.all().filter(name=match_func_name):
            exec(PythonScript.objects.all().get(name=match_func_name).script)
            func_return_value = locals()[match_func_name](*match_func_params)
        else:
            case_logs_obj.result = CaseRunLog.CaseResult.ERROR
            case_logs_obj.logs.append(
                formatter_log('error', '【函数不存在：{}】'.format(func_params[0])))
            raise KeyError
        setattr(params_class, params_key, func_return_value)
        case_logs_obj.logs.append(
            formatter_log('success', '【{}_函数执行返回值为：{}】'.format(match_func_name, func_return_value)))
    else:
        replace_all_func_value(case_logs_obj, inner_funcs, params_value)


def run_func_return_value(match_value, case_logs_obj, inner_funcs):
    match_split = match_value[2:-1].split(',')
    match_func_name = match_split[0]
    match_func_params = str_to_number_and_bool(case_logs_obj, match_split[1:])
    # 函数执行匹配到内部函数
    if inner_funcs.get(match_func_name):
        params_value = inner_funcs[match_func_name](*match_func_params)
    # 函数执行匹配到用户自定义函数
    elif PythonScript.objects.all().filter(name=match_func_name):
        script = PythonScript.objects.all().get(name=match_func_name).script
        params_value = exec_and_return(script)[match_func_name](*match_func_params)
    # 没有配置到函数,用户脚本错误
    else:
        case_logs_obj.result = CaseRunLog.CaseResult.ERROR
        case_logs_obj.logs.append(formatter_log('error', '【函数不存在：{}】'.format(match_func_name)))
        raise KeyError
    return params_value


def replace_all_func_value(case_logs_obj, inner_funcs, params_value, f_pattern=r'f{.*?}'):

    def inner_replace(match):
        return str(run_func_return_value(match.group(0), case_logs_obj, inner_funcs))

    if isinstance(params_value, str):
        return re.sub(f_pattern, inner_replace, params_value)
    else:
        return ast.literal_eval(re.sub(f_pattern, inner_replace, str(params_value)))


def set_user_params(params_class: object, case_logs_obj, params: list, inner_funcs, f_pattern=r'f{.*?}'):
    """
    设置用户全局变量
    """
    for params_obj in params:
        params_key = params_obj['key']
        params_value = params_obj['value']
        params_value = single_replace_data(params_class, case_logs_obj, params_value)
        # 返回的非字符串类型,不用在进行函数执行匹配
        if not isinstance(params_value, str):
            setattr(params_class, params_key, params_value)
            case_logs_obj.logs.append(formatter_log('success', '【{}_用户变量值为：{}】'.format(params_key, params_value)))
            continue

        match_all = re.findall(f_pattern, params_value)
        # 只匹配到了一个执行函数并且匹配函数的字符串长度和原字符串长度相等
        if match_all and len(match_all) == 1 and len(params_value) == len(match_all[0]):
            params_value = run_func_return_value(match_all[0], case_logs_obj, inner_funcs)
        # 匹配到执行函数
        elif match_all:
            params_value = replace_all_func_value(case_logs_obj, inner_funcs, params_value)
        # 用户参数为固定值
        else:
            params_value = params_value
        setattr(params_class, params_key, params_value)
        case_logs_obj.logs.append(formatter_log('success', '【{}_用户变量值为：{}】'.format(params_key, params_value)))


def single_replace_data(params_class: object, case_logs_obj, original_data, pattern=r'\${.*?}'):
    match_all = re.findall(pattern, original_data)
    if match_all and len(match_all) == 1 and len(original_data) == len(match_all[0]):
        return get_attr(params_class, case_logs_obj, match_all[0][2: -1])
    else:
        return replace_params_class_data(params_class, case_logs_obj, original_data)


def get_attr(params_class: object, case_logs_obj, attr_name):
    try:
        attr_value = getattr(params_class, attr_name)
        case_logs_obj.logs.append(formatter_log('success', '【${' + attr_name + '}' + f' 替换值为：{attr_value}】'))
    except AttributeError as e:
        case_logs_obj.result = CaseRunLog.CaseResult.ERROR
        params_attr = {key: value for key, value in params_class.__dict__.items() if not key.startswith('__')
                       and not key.endswith('__')}
        case_logs_obj.logs.append(formatter_log('error', '【${' + attr_name + '}' + f'替换失败, 请参考已存在变量{params_attr}】'))
        raise e

    return attr_value


def replace_params_class_data(params_class: object, case_logs_obj, original_data, pattern=r'\${.*?}'):
    """
    替换用例中URL， Method， Headers， Data， Params，Json， 断言中的数据，通过正则， 默认的替换规规是${}
    """
    def inner_replace(match):
        match_group = match.group(0)
        params_class_attr = match_group[2: -1]
        attr_value = get_attr(params_class, case_logs_obj, params_class_attr)
        return str(attr_value)

    if isinstance(original_data, str):
        return re.sub(pattern, inner_replace, original_data)
    else:
        return ast.literal_eval(re.sub(pattern, inner_replace, str(original_data)))


def set_func_extract(return_value, extract, case_logs_obj, params_class):
    if isinstance(return_value, tuple):
        {setattr(params_class, extract_obj.key, return_value[index]):
         case_logs_obj.logs.append(formatter_log('success', '【{} 被设置为: {}】'.format(extract_obj.key,
                                                                                   return_value[index])))
         for index, extract_obj in enumerate(extract)}
    elif extract:
        setattr(params_class, extract[0].key, return_value)
        case_logs_obj.logs.append(formatter_log('success', '【{} 被设置为: {}】'.format(extract[0].key, return_value)))